{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Explore IBM Q\n",
    "Exploring the backend data visually on IBM Q Experience is a handy but strictly manual process. Sometimes you might want to include backend information when running your programs, for example to select the appropriate backend or dynamically apply your gates on the “best” qubits. To do this you need to dig into the available backend information by using Qiskit."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Let's start by importing the IBMQ method and load our account."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "from qiskit import IBMQ\n",
    "\n",
    "IBMQ.load_account()\n",
    "provider = IBMQ.get_provider()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Then, let's pick the least busy available backend for our exercise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "from qiskit.providers.ibmq import least_busy\n",
    "backend = least_busy(provider.backends(operational=True, simulator=False))\n",
    "#backend = provider.get_backend('ibmq_vigo')\n",
    "print(\"Selected backend:\",backend.status().backend_name)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Let's start by taking a look at the backend configuration details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "backend.configuration()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "We can for example take a look at the number of qubits for the backend, and the supported basis gates, which are the gates supported directly on the chip."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Number of qubits:\", backend.configuration().n_qubits)\n",
    "print(\"Basis gates:\", backend.configuration().basis_gates)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Note: CX, entanglement\n",
    "\n",
    "Another aspect of the backend that is important is the interconnectivity between the individual qubits. Not all qubits can communicate directly with all other qubits. The coupling map illustrates which qubits can communicate directly with which, from the first qubit to the second. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "backend.configuration().coupling_map"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Other interesting information is the number of maximum shots you can run your quantum program and the max number of experiments you can run on the backend per day. It might also be interesting to know the number of pending jobs for the selected backend. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Max shots:\", backend.configuration().max_shots)\n",
    "print(\"Max experiments:\", backend.configuration().max_experiments)\n",
    "print(\"Pending jobs:\", backend.status().pending_jobs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "You can also use some simple Python scripting to compare the available backends based on different criteria."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "# Get all available and operational backends.\n",
    "available_backends = provider.backends(operational=True)\n",
    "\n",
    "# Fish out criteria to compare\n",
    "print(\"{0:20} {1:<10} {2:<10} {3:<10}\".format(\"Name\",\"#Qubits\",\"Max exp.\",\"Pending jobs\"))\n",
    "print(\"{0:20} {1:<10} {2:<10} {3:<10}\".format(\"++++\",\"+++++++\",\"++++++++\",\"++++++++++++\"))\n",
    "\n",
    "for n in range(0, len(available_backends)):\n",
    "    available_backend = provider.get_backend(str(available_backends[n]))\n",
    "    print(\"{0:20} {1:<10} {2:<10} {3:<10}\"\n",
    "          .format(available_backend.name(), available_backend.configuration().n_qubits,\n",
    "                                  available_backend.configuration().max_experiments,\n",
    "                                  available_backend.status().pending_jobs))\n",
    "    \n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "We can now take a look at the physical properties of the selected backend. Let's start by collecting everything in one big heap."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "backend.properties()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "From this mess, which contains all relevant hardware information for the backend, we can dig out the meat. Let us start with some simple data such as the backend name."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Name of the backend:\",backend.properties().backend_name)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Other properties involve more complex information, such as the performance information for the qubits. Let us take a look at the available qubit information for this backend, which is returned as a Python list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "backend.properties().qubits"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Again, let us dig in an and take a look at some of the available qubit data, such as the T1 and T2 decoherence values, the frequency, and the readout error for the qubits. For this exercise we can write a simple Python `for` loop that prints the `name`, `value`, and `unit` for the relevant data entry for each qubit. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "for q in range (0, backend.configuration().n_qubits):\n",
    "    print(\"Qubit\",q,\":\")\n",
    "    for n in range (0, len(backend.properties().qubits[0])):\n",
    "        print(backend.properties().qubits[q][n].name,\n",
    "              \"=\",\n",
    "              backend.properties().qubits[q][n].value,backend.properties().qubits[q][n].unit)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "In addition to the Qiskit way of pulling out the data you can also use the graphical Jupyter view of this information. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false,
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "from qiskit.tools.jupyter import *\n",
    "%qiskit_backend_monitor backend"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "Depending on the type of quantum program that you are writing, certain aspects of the backend might be important to your, and you might want to include these directly when you code your program. For example, you might be interested in the qubits with the smallest gate errors and readout errors, or if you are running deep circuits you might be interested in long T1 and T2 times. Another important piece of information might be the quality of the CNOT gates supported by the chip. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "For more assistance on the backend, use `help(backend)`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "help(backend)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
